This is a real-money trading system. The bar is higher than for most codebases, and the
reasons are in CLAUDE.md §5 and §6. Read those first.
- Read the governing manual. Every block has one; it is named in the module header. The manuals encode the constraints that make the design correct and fast. Skipping them produces code that synthesizes, misses timing, and blows the latency budget.
- State the latency budget in nanoseconds and cycles, in the module header. A block without a budget is not reviewable.
- State the resource budget — LUT/FF/BRAM/URAM/DSP — in the same header.
Full detail in manuals/00-foundations/03-hdl-and-rtl-coding.md.
- Synthesizable SystemVerilog IEEE 1800-2017.
logiconly, neverreg/wire. always_ff/always_combonly. Never barealways.<=in ff,=in comb.- No latches. Default assignments open every
always_comb; everycasehas adefault. - Synchronous active-high
rst. Reset control state only, never datapath registers. - Registered outputs by default; exceptions justified in a comment.
- All literals sized (
8'd5), all parameters typed (parameter int unsigned). - Named generate blocks. One module per file, filename == module name.
`default_nettype noneat the top,`default_nettype wireat the bottom.- SVA assertions inside
`ifndef SYNTHESISon every stream interface and invariant. - No division, no modulo, no floating point.
CDC uses only the sanctioned primitives in rtl/common/. Hand-rolling a
synchronizer is a review failure, not a style disagreement — the failure mode is a design
that works for months and then corrupts one order.
python3 scripts/validate.py --ignore-category broken-link # must exit 0
./scripts/lint.sh # Verilator, -Wall clean
make -C scripts sim # testbenches pass- Every fast-path module needs a testbench. No exceptions.
- Do not report "done" until place-and-route timing closes.
- Quote WNS/TNS and utilization verbatim from the report. Never estimate them.
- If a latency number was simulated, say "simulated". If measured on hardware, say "measured, N=…". These are not interchangeable and conflating them wastes everyone's time.
Rules can be suppressed, but never silently:
localparam real CLKFB_MULT_F = 12.500; // validate: allow real — vendor MMCM declares it realThe justification is mandatory. A suppression without one is itself reported.
These have a blast radius beyond the file you are editing.
| Change | Requirement |
|---|---|
| Risk limits, order sizing, kill switch | Separate commit, separate review, separate audit entry. Never bundled with other work. |
rtl/pkg/trading_pkg.sv |
System-wide contract. Say so explicitly and update the latency budget in the same commit. |
rtl/fpga_top.sv |
Holds the master latency budget. Any added cycle must be justified in the PR. |
| ITCH/OUCH field offsets | Must be verified against the current spec PDF and the verification recorded. A wrong offset produces a decoder that corrupts some messages silently. |
| Anything touching a live venue | Never. Simulated and UAT endpoints only, until conformance certification is complete. |
The risk gate, the kill switch, gap detection, and the error counters. Removing a check to save a cycle converts a latency problem into a solvency problem.
Venue credentials, comp IDs, session IDs, MPIDs, production IP addresses, or recorded
exchange market data. .gitignore covers the common cases; it is not a substitute for
looking at your diff.
Conventional-Commit prefixes (feat:, fix:, chore:, docs:, refactor:, test:,
build:), imperative mood, subject ≤ 72 chars. The body explains why, not a file-by-file
recap — and in this codebase the why is usually a failure mode being prevented. Say what
it is.