Anulum Overview Host analysis AMP simulation Validation Python API Rust API C/C++ API

Fabric witness RTL

rtl/event_witness.sv connects the timestamp capture to a dual-clock FIFO. This is a simulation-tested stream component. Peripheral event arbitration, AXI/UIO access, coherent processor status snapshots, physical clock constraints and board qualification remain planned. The domain maturity is architecture_only; the capability inventory remains empty.

Event input and record output

Port Clock domain Contract
event_valid, event_code[7:0], cycle_number[31:0] capture One tuple per rising edge when capture_active is high. No backpressure.
capture_active capture Local run reset has released.
counter_ticks[63:0] capture Free-running counter, cleared by run reset; record samples the pre-increment value.
buffer_full capture No new record can be accepted at the current write edge.
event_dropped capture High for each edge rejecting a pending captured record. Consecutive drops keep it high.
overflowed capture Sticky loss flag until run reset.
overflow_count capture Saturating count of rejected records; a lower bound after saturation.
drain_request drain Request the next record; empty requests do not advance the pointer.
drain_valid, drain_record[127:0] drain Registered result of the request at this rising edge; consume only when valid.
drain_empty drain No record currently visible to the read domain.

A source tuple is captured at an edge, then offered to the FIFO at the next capture edge. It retains the original timestamp even if it later crosses into the drain domain. The write status and overflow pulse therefore refer to the pending captured record. The source must supply event codes from rtl/event_codes_pkg.sv and the hash-bound domain snapshot. The host rejects unknown codes, nonzero reserved fields, regressing ticks/cycles and duplicate events. Arbitrate simultaneous source strobes before this input; this module cannot represent two events at one capture edge. Clock crossing of asynchronous source strobes is also upstream.

The 128-bit output is {timebase_ticks[63:0], cycle[31:0], 16'd0, 8'd0, event_code[7:0]}. Write its least significant byte first for the published 16-byte little-endian host format. The record payload is unspecified when drain_valid is low, including during reset.

Capacity and loss

ADDRESS_BITS accepts 1 through 14; capacity is 2**ADDRESS_BITS, with the default 16,384 records. The capture pipeline is not an extra accepted storage slot. A full FIFO preserves stored records and drops the newest pending record. There is no attempt to stall measured source activity. OVERFLOW_COUNTER_BITS accepts 1 through 32, default 32. Invalid parameter values fail elaboration/simulation. Any loss invalidates the run, including after the buffer recovers. The host manifest must record the final capture-domain count before run reset.

The machine-readable event_fifo contract declares the Gray-pointer implementation, drop-newest policy, 32-bit counter and reset policy. The schema requires those policy fields when the implementation is declared; the validator checks a supported power-of-two depth. Earlier v1 domain snapshots without that implementation declaration retain their original sizing contract, so archived hash-bound runs remain interpretable.

Clock crossing and reset

event_record_fifo.sv uses binary pointers with an extra wrap bit, Gray-coded pointers, two destination-clock synchroniser stages, registered full/empty flags and a dual-clock 128-bit memory. Full compares the next write Gray pointer to the synchronised read pointer with its top two bits inverted. Empty compares the next read Gray pointer to the synchronised write pointer. Synchronisation makes flags conservative: newly written data or newly freed space becomes visible after destination-clock edges, rather than immediately. Synchroniser registers carry ASYNC_REG attributes. This follows FIFO style 1 in Clifford E. Cummings, Simulation and Synthesis Techniques for Asynchronous FIFO Design, SNUG 2002.

Both clocks share run_reset_n. Assertion is asynchronous; each clock_reset_release.sv instance waits for two local rising edges before release. A stopped clock remains in reset. Assertion flushes the run, clears the timebase and loss state and prevents old records from becoming valid. RAM contents and the invalid read payload are not cleared, preserving memory inference. Independent reset of one pointer domain is unsupported. Hold the common reset low for the device's required minimum pulse width and meet reset-recovery/removal constraints.

Capturing and draining need no coherent shared clock. Reading capture-domain status from processor logic does require a separate coherent CDC snapshot; synchronising each counter bit separately is insufficient. Gray-bus skew/max-delay constraints, placement of synchroniser stages and RAM inference must be supplied and reviewed in the vendor integration. RTL simulation and generic synthesis do not establish metastability rates, timing closure or physical RAM fit.

Verification

Run the dedicated public-port simulations and real host integration:

.venv/bin/pytest -q tests/test_clock_reset_release.py tests/test_event_record_fifo.py tests/test_event_witness.py
yosys -s rtl/check_witness_memory.ys
yosys -s rtl/check_witness_equivalence.ys
verilator --lint-only --Wall --top-module event_witness rtl/clock_reset_release.sv rtl/event_record_capture.sv rtl/event_record_fifo.sv rtl/event_witness.sv

The FIFO scoreboard exercises full capacity, requests at empty, four complete fill/drain rounds, deterministic random traffic, faster/slower/equal clock rates, queued-data reset and recovery. The witness scoreboard checks every accepted record and its original timestamp, continuous events, 17 counted drops, small-width saturation, recovery and a fresh run. The host tests bind the sources, compiled simulation and binary stream by SHA-256, check 5,000-tick periods and 20-tick loop latency, and mark an actual overflow stream invalid. Those values are test schedules, not board performance claims.

Generic synthesis must retain one 128-bit by 16,384 dual-clock memory at default capacity. Post-optimisation equivalence uses a small FIFO configuration with memory mapping and async2sync normalisation before comparing the prepared and synthesised designs. That proof checks logic transformations under the normalised model; it does not prove physical CDC safety. Board acceptance still requires the same-bitstream tests in MEASUREMENT_PROTOCOL.md.

Simultaneous control strobes

The one-event input remains the generic stream contract. The plant entry point instead uses control_event_capture.sv to snapshot simultaneous strobes before serialization and feeds this same dual-clock FIFO. Its group-queue and combined loss semantics are specified in PLANT_WITNESS.md.