Hardware Engineering · Storage Hardware

Page Program and Block Erase: The Asymmetry That Shapes Flash Software

Why a flash array writes in pages of a few kilobytes but can only be cleared in blocks of hundreds, what that costs in area and time, and how the asymmetry propagates upward.

A flash array cannot overwrite a byte. It cannot overwrite a page either, even though it writes in pages. It can only add charge to cells that are already erased — the charge that shifts a cell’s threshold, as described in how a NAND cell stores a bit — and it can only remove charge from a whole block at once. That is not an implementation detail of a particular controller; it is a property of the array’s wiring, and it forces every piece of software that touches flash to maintain a mapping layer between the addresses it uses and the physical cells that currently hold the data.

The erase is bulk because the source is shared

The cells in a block share a common source line and sit in the same well. Erasing means pulling electrons out of the floating gates, which requires a large field between the control gate and the channel. The practical way to produce it is to hold the control gates of every wordline in the block at a low voltage and raise the well and the source line to a high positive voltage, so the field across the tunnel oxide points in the direction that drives charge out.

Applying that field to the whole well means every wordline in the block must be at the same potential, which means every cell in the block is erased together. There is no way to confine the field to one wordline without giving that wordline its own well, which would multiply the number of wells and the decoder wiring by the number of wordlines in a block. Vendors chose the shared well, and with it a block granularity measured in hundreds of pages: a block holds a fixed number of wordlines, and each wordline holds one or more pages depending on how many bits per cell the part stores.

The block size is therefore a fabrication decision, not a protocol one. Parts in common use have blocks in the range of a few hundred pages to a couple of thousand pages; with pages of 4 KiB to 16 KiB and cells storing two or three bits, a single block holds on the order of a few megabytes. The exact figures vary by generation and vendor, and the host never needs to know them — which is precisely the point of the layer above.

Programming is per page because the bitline is per page

Programming injects charge into selected cells. The mechanism applies a high voltage to the selected wordline and a moderate voltage to the bitlines of the cells that should be programmed, while inhibiting the cells that should not. The bitline is the wire that runs perpendicular to the wordlines and connects one cell in each wordline of the block; it is the only wire that can be driven individually.

That is the constraint. The wordline is shared across all the cells in a row, so it cannot select individual cells. Selection is done by the bitline, and the bitline connects one cell per wordline. A programming operation therefore writes one row of cells across many bitlines — that is, one page — and nothing else. The page is the intersection of the one wordline the decoder selects and the set of bitlines the page buffer drives.

Modern parts use the bitline to select and the wordline voltage to enable, and they interleave pages so that a single wordline holds several pages: with multi-level cells the same wordline’s cells can be programmed in stages, each stage writing a different subset of the bits. This is why a wordline in a triple-level-cell part holds three pages, not one, and why the programming order across those pages matters.

What the asymmetry costs in area

The erase and program circuits occupy different parts of the die. Program needs a charge pump capable of the program voltage and a page buffer holding one page of data with per-bitline latches, plus verify sense amplifiers. Erase needs a pump capable of the higher erase voltage and a decoder that can drive the whole well. Because erase happens at block granularity and program at page granularity, the row decoder must handle the block-level erase select lines for every block, and the number of blocks is large: a die with hundreds of blocks needs a decoder tree with that many leaves.

The area cost of the block structure is not only the decoder. Each block must be isolated from its neighbours during erase, so there are guard structures and select transistors at the block boundaries. In three-dimensional NAND, where cells are stacked vertically and the block is a pillar of strings, the block boundary is a physical trench through the stack, and the number of blocks is limited by how many trenches can be cut without consuming too much of the array. That is one reason three-dimensional parts have larger blocks than planar parts did: the trench overhead is amortised over a taller stack.

The time cost is the more familiar one. An erase operation applies the erase voltage for a period long enough for the slowest cells in the block to reach the erased state, then verifies, and repeats with a higher voltage if some cells are still above the erase threshold. That loop takes milliseconds, compared with the hundreds of microseconds a page program takes and the tens of microseconds a read takes. The ratio is what makes a small random write on a full block expensive: the controller must erase a block to free space, and the erase costs more than the program it enables and far more than the read that would have served the same bytes.

The forbidden case: programming a programmed cell

A program operation on a cell that already holds charge would add to it, moving the threshold above the intended state. There is no way to partially remove charge from a single cell, so the write cannot be corrected in place. The array does not forbid the operation electrically; it simply produces a cell in the wrong state. Controllers therefore refuse it at the protocol level: the NAND command set’s program operation requires the target page to be erased, and a program to a page that is not erased produces undefined results. Most parts report the failure in a status register after the fact, and some detect it during the program-verify loop.

The prohibition is what forces the write path to be copy-on-write at the page level. A page that must be changed cannot be changed; a new page must be programmed elsewhere, and the mapping must be updated to point there. That is true even for a single-byte change. The mapping layer that performs this is described in flash translation layers and the mapping they maintain, and the write amplification it produces is the reason a random-write workload can cost many times the bytes the host writes.

Partial pages and the tail of a write

A page program can be split. The command set allows a program operation to be issued, the data latched into the page buffer, and a second program to the same page with more data appended, as long as the page has not yet been committed and the address continues where the first ended. This exists so that a host writing a page in several transfers does not have to buffer the whole page before starting. The consequence is that a page can be partially programmed: some bitlines hold valid data and others hold whatever was in the buffer.

A partially programmed page cannot be appended to for the unprogrammed portion: each program pass applies the program voltage to every cell on the wordline, including those already at their target, so parts specify a limit on the number of partial programs a page may receive. Controllers therefore treat a partially programmed page as closed and allocate a fresh page for the remainder, which is one source of the internal write amplification that shows up when the host writes in sizes smaller than the page.

/* Physical write path for a logical write, showing why a small write
   still consumes a whole page and may trigger a block erase. */
struct page { uint32_t block; uint16_t page_no; uint32_t len; };

static struct page *alloc_page(struct ftl *f);

int ftl_write(struct ftl *f, uint64_t lba, const void *buf, size_t len)
{
    size_t done = 0;
    while (done < len) {
        struct page *p = ftl_open_page(f, lba + done);   /* current page for this stream */
        if (!p) {
            p = alloc_page(f);        /* may erase a block and relocate its live pages */
            if (!p)
                return -ENOSPC;
        }
        size_t room = f->page_size - p->len;
        size_t n = len - done < room ? len - done : room;
        nand_program(p, p->len, (const char *)buf + done, n);
        p->len += n;
        done += n;
        if (p->len == f->page_size)
            ftl_close_page(f, p);     /* a full page cannot be reopened */
    }
    return 0;
}

The loop shows the shape of the problem: the unit of allocation is a page, the unit of reuse is a block, and the two do not coincide. A write that crosses a page boundary consumes two pages, the second of which is mostly empty but can never be filled later once closed. Sequential writes fill pages exactly and waste nothing; random writes at host block granularity waste most of each page.

Endurance is counted in erases

Every erase cycle damages the tunnel oxide slightly, trapping charge and creating interface states. The damage accumulates and eventually shifts the erased threshold high enough, or the programmed distribution wide enough, that the error-correcting code can no longer correct the page. Endurance ratings are therefore expressed in program-and-erase cycles per block, not in bytes written, and the number of cycles a block has seen is the number the controller tracks for wear levelling.

Because a block erase is required to reclaim any space, the number of host bytes a drive can absorb is the endurance rating in cycles multiplied by the block size and by the number of blocks, divided by the write amplification the workload induces. A drive built from parts rated for a few thousand cycles, with blocks of a few megabytes, therefore has a total-write budget in the range of a few petabytes for sequential traffic, and far less for traffic that forces relocation. The arithmetic is the reason write amplification, not raw endurance, is the number a storage engineer should care about — and the reason the mapping layer’s choice of when to relocate data is a first-order design decision rather than an optimisation.

References

  1. NAND Flash Solid State Storage for the Enterprise — SNIA Solid State Storage Initiative, technical white paper
  2. Inside Solid State Drives (SSDs) — R. Micheloni, A. Marelli, K. Eshghi, Springer
  3. JESD218: Solid-State Drive (SSD) Requirements and Endurance Test Method — JEDEC
  4. A Survey of Flash Translation Layer — Journal of Systems Architecture, survey of FTL designs