Hardware Engineering · Storage Hardware
Error Correction in NAND: From Hard Bits to Soft Read Retry
How BCH and LDPC codes absorb the raw bit error rate of a flash page, why soft-decision decoding needs several reads at shifted reference voltages, and what happens when correction runs out.
A flash page leaves the die with errors in it. Not occasionally: always, at a rate that rises with every program-and-erase cycle, with every read of a neighbouring page, and with every month the data sits unpowered. The error-correcting code is not a safety net for a rare event. It is the mechanism that makes the storage medium usable at all, and its margin is what determines the endurance rating printed on the part. The margin itself comes from the spacing between threshold states, described in how a NAND cell stores a bit.
The raw bit error rate is a design parameter
Raw bit error rate, usually abbreviated RBER, is the fraction of bits that differ from what was programmed, measured before any correction. Fresh single-level cells can sit in the range of one error in a hundred thousand bits or better. A worn triple-level cell near the end of its rated cycles is two or three orders of magnitude worse, and a cell that has been sitting unpowered at elevated temperature can be worse still.
The number matters because it sets the code rate the controller must carry. If a page holds n bits and the code can correct t of them, the code rate is the fraction of the page that carries user data. A stronger code corrects more errors and leaves less room for data, so the drive’s usable capacity falls. Controllers therefore pick a code whose correction capability covers the worst RBER the part is expected to reach at end of life, with a margin for the tails of the distribution.
Errors are not uniformly distributed across a page. Retention loss is worse on cells that were programmed to the highest threshold states, because those cells have the most charge to lose and the smallest gap to the next state down. Program disturb is worse near the wordlines that were programmed last. Read disturb accumulates on the wordlines that share a block with a page that is read repeatedly, because those cells sit under the pass voltage while the selected wordline is being sensed. A page’s error count is therefore a function of its position in the block and of the block’s history, and the controller’s health model tracks those factors per block rather than per drive.
BCH and the hard-decision ceiling
The traditional code for flash is BCH, a cyclic code over a binary extension field. Its parameters are stated as (n, k, t): a codeword of n bits carrying k bits of data and capable of correcting any pattern of up to t errors. The guarantee is deterministic — any t errors, in any positions — which is why BCH was the natural choice for a medium whose errors were poorly characterised.
The decoder computes syndromes from the received word, solves for the error locator polynomial, finds its roots, and flips the indicated bits. If the number of errors exceeds t, the decoder either fails outright or, worse, produces a valid codeword that is not the one that was written. The latter is a miscorrection, and it is why a strong checksum is layered above the code: a miscorrected codeword that passes BCH still fails a CRC, which is what allows the controller to distinguish “corrected” from “plausibly corrected”.
The ceiling is the reason BCH lost ground. A hard-decision decoder sees only the bits the sense amplifiers produced; the analogue distance between a cell’s threshold and the reference voltage is discarded. At high RBER, most of the information that would let a decoder tell a reliable bit from a marginal one is thrown away before decoding begins. BCH’s correction capability is fixed by its parity budget, and once the RBER exceeds what that budget can absorb, no amount of retrying helps.
Soft information and LDPC
LDPC codes, invented by Gallager and rediscovered decades later, are defined by a sparse parity-check matrix and decoded by iterative message passing over a bipartite graph of variable nodes and check nodes. The decoder does not need a fixed correction radius; it improves as the number of iterations grows, and it can use soft inputs.
A soft input is a log-likelihood ratio: for each bit, the logarithm of the probability that it is a zero divided by the probability that it is a one. A hard read gives ratios of large magnitude with the sign of the decision — the decoder is told the bit is certainly this value. A soft read, taken at a reference voltage offset from the nominal, gives a second opinion: cells whose threshold lies between the two references are marginal, and the decoder is told their bits are uncertain.
With that information, a soft-decision LDPC decoder can correct far more errors than a hard-decision BCH decoder of the same rate, because it is not restricted to flipping a bounded number of bits — it is finding the most probable codeword given a noisy observation. The cost is latency and complexity. Iterative decoding takes many passes over the codeword, and each soft read is an extra array access at a different reference voltage, so the read path becomes a search rather than a single operation.
# Coarse log-likelihood ratios from two reads at different reference voltages.
# LLR = log(P(bit is 0) / P(bit is 1)); a low threshold reads as a 1.
def soft_level(v_cell, v_nominal, v_offset):
lo, hi = sorted((v_nominal, v_offset))
if v_cell < lo:
return -3.0 # below both references: strongly a 1
if v_cell < hi:
return -0.7 # between them: weakly a 1
return 3.0 # above both: strongly a 0
The sketch is intentionally trivial; the real decoder receives a calibrated ratio per cell, derived from the measured threshold offset and the shape of the programmed distribution, not a coarse three-level label. The point is that the second read costs another array access and buys information the first read did not contain.
Read retry as a search over reference voltages
When a page fails to decode at the nominal reference voltages, the controller does not immediately give up. It retries with a different set of references. The offsets come from tables the vendor supplies with the part, indexed by a condition code the controller selects: high retention, read disturb, program disturb, or a general fallback. Each table entry shifts several references at once, because the failure mode is usually a shift of the whole distribution rather than a single boundary.
Retry proceeds in stages. First a handful of candidate offsets, each a single read attempt. If none decode, the controller escalates to soft reads: pairs or triples of reads at neighbouring offsets, producing the log-likelihood ratios the soft decoder needs. The escalation is bounded (a fixed number of attempts before the page is declared uncorrectable) because each attempt costs an array access and the controller’s time budget for a host read is finite.
The threshold-tracking loop that keeps the nominal references calibrated is the same mechanism running in the background. A controller can periodically read a page with several offsets and use the shape of the resulting error counts to estimate where the distribution boundary currently lies, then store the estimate per block. That estimate is what makes the first read attempt succeed most of the time, so the expensive retry path is rarely entered.
When correction runs out
An uncorrectable error is a page whose data cannot be recovered by any code the controller can apply. The controller marks the block, stops using it for new writes, and returns an error to the host for the affected logical addresses. If the drive carries die-level redundancy — a parity scheme across the dies of a channel, analogous to RAID — the failed page can sometimes be reconstructed from the surviving pages of the stripe, which converts a data-loss event into a relocation.
The rate at which uncorrectable errors occur is what the endurance specification is really about. JEDEC’s endurance standard defines classes of drives by their workload and their retention requirement, and the retention requirement is a statement about how much margin the code must retain after the rated cycles. A drive rated for a heavier workload must either use a stronger code, a larger over-provisioning factor, or a better process. Those are the three levers, and they trade against capacity and cost.
The margin also decays after the drive stops being written. Retention loss continues while the drive is unpowered, and the temperature during storage dominates the rate. A drive that passes its endurance test at end of life can still lose data if it is stored warm for long enough, which is why the standard specifies the storage temperature alongside the retention period and why the same part can carry different ratings for client and enterprise use. The controller’s background scrubbing, reading blocks and rewriting them before their margin is exhausted, is the mitigation, and its cost is measured in write amplification, which the flash translation layer must account for.
References
- Low-Density Parity-Check Codes — R. G. Gallager, MIT Press, 1963
- Error Control Coding — S. Lin, D. J. Costello, Prentice Hall
- Error Characterization, Mitigation, and Recovery in Flash-Memory-Based Solid-State Drives — Y. Cai, S. Ghose, E. F. Haratsch, Y. Luo, O. Mutlu, Proceedings of the IEEE, 2017
- JESD218: Solid-State Drive (SSD) Requirements and Endurance Test Method — JEDEC