Execution trace recording for systolic array runs.
Every tick optionally emits a Trace.Event per PE, recording inputs,
outputs, and state transitions. The trace is:
- optional -- disabled by default to avoid overhead
- complete -- when enabled, every PE at every tick is recorded
- deterministic -- same inputs always produce the same trace
Storage and ordering
Internally, events are stored in reverse-chronological order
(newest event first) so that recording is O(1) amortized. The query
helpers at/2 and for_coord/2 return their results in
chronological order (oldest first), matching the order in which
events were recorded. If you access trace.events directly, call
Enum.reverse/1 to obtain chronological order.
Use cases
- Debugging PE behaviour
- Visualising data flow (e.g. in a livebook)
- Regression testing (compare traces across runs)
Summary
Functions
Filters trace events by tick number, returned in chronological order.
Filters trace events by PE coordinate, returned in chronological order.
Creates a new trace from a list of events.
Records a single PE step as a trace event.
Types
@type event() :: ExSystolic.Trace.Event.t()
@type t() :: %ExSystolic.Trace{events: [event()]}
Functions
@spec at(t(), non_neg_integer()) :: [event()]
Filters trace events by tick number, returned in chronological order.
Examples
iex> e0 = %ExSystolic.Trace.Event{tick: 0, coord: {0,0}, inputs: %{}, outputs: %{}, state_before: 0, state_after: 0}
iex> e1 = %ExSystolic.Trace.Event{tick: 1, coord: {0,0}, inputs: %{}, outputs: %{}, state_before: 0, state_after: 0}
iex> trace = ExSystolic.Trace.new() |> ExSystolic.Trace.record(e0) |> ExSystolic.Trace.record(e1)
iex> [event] = ExSystolic.Trace.at(trace, 0)
iex> event.tick
0
@spec for_coord(t(), ExSystolic.Grid.coord()) :: [event()]
Filters trace events by PE coordinate, returned in chronological order.
Examples
iex> e0 = %ExSystolic.Trace.Event{tick: 0, coord: {0,0}, inputs: %{}, outputs: %{}, state_before: 0, state_after: 0}
iex> e1 = %ExSystolic.Trace.Event{tick: 1, coord: {0,0}, inputs: %{}, outputs: %{}, state_before: 0, state_after: 0}
iex> trace = ExSystolic.Trace.new() |> ExSystolic.Trace.record(e0) |> ExSystolic.Trace.record(e1)
iex> ticks = ExSystolic.Trace.for_coord(trace, {0,0}) |> Enum.map(& &1.tick)
iex> ticks
[0, 1]
Creates a new trace from a list of events.
Examples
iex> trace = ExSystolic.Trace.new([])
iex> trace.events
[]
Records a single PE step as a trace event.
Events are prepended for O(1) amortized recording. Use at/2 or
for_coord/2 to retrieve in chronological order.
Examples
iex> trace = ExSystolic.Trace.new()
iex> event = %ExSystolic.Trace.Event{tick: 0, coord: {0,0}, inputs: %{}, outputs: %{}, state_before: 0, state_after: 7}
iex> trace2 = ExSystolic.Trace.record(trace, event)
iex> hd(trace2.events).state_after
7
iex> hd(trace2.events).tick
0