You press one button — the tools call it "Generate Bitstream" — and somewhere between twenty
minutes and a long lunch later, your RTL is a working chip. In that window, software solves a chain
of genuinely hard problems: rewriting your logic into
The flow takes two kinds of input. The first is your RTL. The second — every bit as important — is the constraints file, which tells the tools two things the RTL cannot:
The pin assignments anchor your ports to the physical package (get one wrong and your LED design flashes a motor driver instead). The clock definition is the contract the whole flow works to satisfy: every register-to-register path must settle within one period. Without it the tools have no target — and no way to tell you they missed.
Step through the pipeline — and notice that every stage hands a concrete artefact to the next, each one a little closer to physical:
Two of these stages are combinatorial optimisation problems of monstrous size. Placement must position hundreds of thousands of cells so that connected ones sit near each other — shorter wires mean less delay. Routing must then realise every net using the finite wire segments that actually exist, fighting congestion where too many nets want the same channel. Both are NP-hard; the tools attack them with heuristics, randomness and sheer iteration, which is why the same design can take different times (and reach different clock speeds!) on different runs.
Here is placement in miniature: six blocks, seven nets, a 4×4 grid. Scatter the blocks randomly, measure the total wire length (Manhattan distance per net), then greedily swap pairs whenever a swap helps — which is, in spirit, exactly what the real tools do at nation-state scale (Module 5 returns to this problem for ASICs):
Watch the wirelength drop. Real placers add cleverness on top — simulated annealing to escape local minima, analytic solvers, timing-driven weighting so the critical nets get shortest — but the objective you just optimised is the genuine article.
A flow run does not end with "done"; it ends with reports, and professionals read
them the way pilots read instruments. The utilization report you met
Negative slack — the dreaded red — means the critical path is too slow for the requested clock, and the loop begins: read the path listing (it names every LUT and wire on the offending path), then fix it — restructure the RTL (fewer logic levels, add a pipeline register), help the placer with a floorplan hint, or, honestly, lower the clock. Rerun. Read again. This edit–compile–report loop is minutes to hours on an FPGA. Hold that thought for the ASIC modules, where a full physical iteration takes weeks and the final "rerun" — a mask set — costs millions: the FPGA is the rehearsal stage where iteration is nearly free, which is precisely why industry prototypes ASIC designs on FPGAs first.
For most of FPGA history the answer was a flat no: bitstream formats were trade secrets, so only Xilinx's or Altera's tools could target their chips. Then, in the mid-2010s, came one of open source's great reverse-engineering feats: the IceStorm project documented the bitstream of Lattice's little iCE40 parts bit by bit, and a complete free flow snapped into existence around it — Yosys for synthesis, nextpnr for place and route. The whole loop, RTL to configured silicon, runs in seconds for small designs, which transformed FPGA tinkering: continuous-integration servers now build hardware on every git push, exactly like software. The community has since opened up several larger families, and the vendors — who once sent lawyers after bitstream researchers — now sponsor some of the work. If you want to see a placer sweat, nextpnr even ships a GUI that animates the placement converging.
The most seductive line in any log file is "Bitstream generation completed successfully." It means only that the tools produced a circuit — not that the circuit keeps up with your clock. A design can sail through every stage and still carry negative slack: the flow will happily emit a bitstream for a design that fails timing (some tools print a warning; none refuse). Load it and you get the cruellest kind of bug — hardware that mostly works, then corrupts a value when the temperature rises or one particular data pattern exercises the critical path, because a register is being clocked before its input has settled. The discipline is absolute: read the timing summary after every run, and treat "met timing, worst slack +0.3 ns" — not "bitstream generated" — as the definition of success. Where that number comes from is the subject of static timing analysis, coming in the next module.