# Verilator RTL Conventions ## Verilator Version **5.046** (Fedora package). Requires **C++14** or later. ## Compile Arguments **Do NOT** add `-CFLAGS -std=c++11`. Verilator 5.046 uses C++14 features (`""s` string literal). Let Verilator use its default C++ standard. Correct base command: ``` verilator -Wall --cc --build --timing --exe --top-module ``` ## Include Paths All Verilog files use paths relative to project root (`~/Dev/mlkem`). Pass `+incdir+` to Verilator so `\`include "sync_rtl/common/defines.vh"` resolves. ## Clock Period 100MHz = 10ns. Defined centrally in `sync_rtl/common/defines.vh`: ```verilog `define CLK_PERIOD 10.0 ``` ## Testbench Timing Protocol (Verilator C++) ### Critical Rule: Always advance at least one posedge before checking signals Setting a DUT input (e.g., `dut->valid_i = 1`) does NOT take effect until `dut->eval()` is called. A posedge requires: `dut->clk = 1; dut->eval();`. ### pipeline_reg valid/ready timing The `pipeline_reg` module's valid_o is HIGH for exactly ONE cycle between the posedge that captures data and the next posedge that consumes it (when ready_i=1). Correct read pattern: ```cpp // 1. Drive input + posedge → data captured, valid_o → 1 dut->valid_i = 1; posedge(dut); // dut->clk = 1; eval(); dut->clk = 0; eval(); dut->valid_i = 0; // 2. Read result NOW (valid_o is high, sum is valid) printf("RESULT: %03X\n", dut->sum & 0xFFF); // 3. Consume with next posedge → valid_o → 0 posedge(dut); ``` ### Anti-pattern: while(!dut->ready_o) without eval Setting valid_i=1 and immediately checking ready_o in a while loop that starts with NO eval() will skip the first clock edge entirely if ready_o is already 1. Use do-while to guarantee at least one edge: ```cpp dut->valid_i = 1; do { posedge(dut); } while (!dut->ready_o); ```