FPGA Craps Simulation Engine
A hardware Monte Carlo engine that plays 10,000 complete craps games in half a millisecond on a Basys 3, with results checked against the analytical win probability rather than against itself.
- Period
- Summer 2026
- Role
- Solo
- Stack
- Verilog · Vivado · Basys 3 · LFSR
- Source
- Repository
- 100 MHz
- simulation engine
- 10K games
- in 0.5 ms
- 0.3σ
- of analytical expectation over 1M games
The problem
Monte Carlo simulation is embarrassingly parallel and almost entirely control flow: roll, compare, branch, update state, repeat. That combination is unremarkable on a CPU and well-suited to hardware, where the whole game loop can be dedicated logic instead of instructions.
Craps is a good target because it has a known analytical answer. That matters more than the speed. A simulation engine that runs fast but is subtly wrong produces confident nonsense, and there’s usually no way to tell from the output. Here there is: the true win probability is derivable in closed form, so the hardware can be checked against mathematics rather than against another implementation of the same assumptions.
What I built
A 100 MHz Verilog simulation engine with:
- Dual FSMs — one for main game control, one for side-bet control. Craps has a main line bet with pass/come-out/point phases and separate side bets with their own rules; the two resolve on different schedules, and forcing both into one state machine would have produced a large FSM that was hard to verify. Two smaller machines are each independently checkable.
- 23-bit and 24-bit LFSRs for dice generation. Two different lengths, so the two dice don’t share a sequence — same-length LFSRs seeded differently still risk correlation patterns, and correlated dice would bias every statistic the engine produces.
- Dedicated game-state and win-counting logic, so a completed game costs a small fixed number of cycles rather than a variable-length software loop.
Result: 10,000 complete games in 0.5 ms.
Verification
The engine is checked against theory, not against a second implementation:
Win probability across 1,000,000 simulated games lands within 0.3σ of the analytical expectation. The σ framing is deliberate — a Monte Carlo engine is stochastic, so “matches the expected value” is not a meaningful pass condition. The right question is whether the observed result sits within the sampling error you’d expect for that number of trials. At 1M games, 0.3σ says the engine is behaving like a correct simulation of craps and not merely like something producing a number near the right one.
Self-checking Verilog testbenches cover the game logic itself, so rule errors are caught directly rather than being inferred from a statistical mismatch at the end.
| Check | Method | Result |
|---|---|---|
| Game rules | Self-checking testbenches | Directed cases pass |
| Statistical correctness | 1M games vs. analytical | Within 0.3σ |
| Throughput | On-hardware timing | 10K games / 0.5 ms |
What I’d do next
- Test the LFSRs directly. Statistical agreement at the output implies the dice are well-behaved, but I never characterized the generators themselves. Period and correlation checks would make that an established property rather than an inference.
- Parameterize the bet rules. The side-bet FSM encodes one rule set; making it configurable would turn this from a craps engine into a small betting-game engine.
- Parallel game instances. One game at a time is what fits the current control structure, but the workload is parallel by nature and the part has room.