Write a cycle-level contract
Before generating a counter or FIFO, specify widths, reset polarity, latency and boundary behavior. For a FIFO, define simultaneous reads and writes, empty state and full state separately. Ask AI for RTL and a test table derived from that contract. Review pointer arithmetic, signedness and state-update conditions explicitly, even when the generated code looks familiar.
Start with lint
The example below assumes a file named top.v containing module top. Supply your project's actual sources and include paths. Investigate width, undriven-signal and incomplete-assignment diagnostics. Ask AI to explain a specific diagnostic and propose a minimal fix. Run the check again after the patch; broad warning suppression removes useful review evidence.
verilator --lint-only -Wall --top-module top top.vCompare simulation and synthesis evidence
Run a self-checking simulation, then Yosys synthesis for the target device. Review hierarchy, memory and latch messages alongside resource statistics. Verilator and Yosys serve different purposes; passing one does not replace the other. Check support for the HDL subset you use. Successful synthesis does not establish that timing constraints will be met after placement and routing.
Keep the acceptance evidence
Save tool versions, file lists, parameters, test seeds and reports. Exercise reset during traffic and every boundary case in the contract. For a bug fix, retain a test that failed before the change. Before FPGA release, add pin constraints and timing results from the implementation flow for the actual target.
Checks before accepting the result
Lint, tests and synthesis complement each other; none alone proves the design correct.