functional coverage
Imagine you wrote a detailed test plan for a vending machine: it should accept exact change, give change back, reject foreign coins, handle a sold-out slot, recover after a power blip mid-purchase. Now you hand the machine to a tester and ask, "Which of those situations did you actually try?" Functional coverage is the scoreboard that answers that question — a running tally of which intended behaviors and scenarios your tests genuinely exercised, ticked off one by one as they happen during simulation.
More precisely, functional coverage measures progress against a coverage model you write by hand: a list of the interesting conditions a correct design must handle. You declare cover points (track every value this opcode field took), bins (group those values into buckets that matter — min, max, overflow), and cross coverage (did a read and a write ever collide in the same cycle?). As your tests run — often a flood of constrained-random stimulus in a UVM testbench — the simulator records which bins got hit. The percentage of bins covered is your honest answer to "are we done testing yet?"
The crucial thing it is not is code coverage's cousin in name only. Code coverage (lines, branches, toggles) only tells you which RTL got executed — you can hit 100% of the lines while never testing the one back-to-back-request scenario where the bug actually lives. Functional coverage tracks intent: the scenarios you decided up front were worth checking. High code coverage with low functional coverage means you ran a lot of code blindly; high functional coverage with passing checks is what lets a team sign off with a straight face.
covergroup cg @(posedge clk);
cp_op : coverpoint opcode { bins arith[] = {ADD, SUB}; bins mem[] = {LD, ST}; }
cp_burst : coverpoint burst { bins single = {0}; bins burst = {1}; }
cross cp_op, cp_burst; // did each op happen during a burst?
endgroupA covergroup samples the opcode and burst flag each clock; the cross asks whether every op was exercised during a burst transfer.
"Coverage closure" — driving functional coverage to (near) 100% — is a project milestone, not a guarantee of correctness. Coverage tells you a scenario was visited; it is your checkers and assertions that decide whether the design behaved correctly when it got there. A covered bin with no checking is a box ticked on an unread page.