A directed communication channel between two processing-element ports.
Each link models a FIFO buffer with configurable latency and capacity. Data written to a link at tick T becomes readable at tick T + latency.
Mental model
Think of a link as a pipeline: values enter one end and exit the other
after a fixed number of clock ticks. The buffer field holds a
:queue whose length never exceeds capacity.
Reserved values
The atom :empty is reserved to denote "no value present" when a
link buffer is drained. PEs receiving :empty on an input port should
treat it as "no input this tick". Do not write :empty as a payload
value into a link, or use it as a meaningful PE state symbol -- it
will collide with the empty-buffer signal.
Determinism guarantee
Links are purely functional data structures. Every operation returns a new link struct; nothing is mutated. Two links with identical fields always produce identical behaviour, which makes execution fully deterministic.
Summary
Functions
Returns whether the link buffer is empty.
Creates a new link between two endpoints.
Peeks at the oldest value without removing it.
Reads and removes the oldest value from the link buffer (FIFO).
Returns the number of items currently in the buffer.
Advances the link by one tick: drains the buffer if latency has expired.
Writes a value into the link buffer.
Types
@type port_name() :: atom()
@type t() :: %ExSystolic.Link{ buffer: :queue.queue(), capacity: pos_integer(), from: endpoint(), latency: pos_integer(), to: endpoint() }
Functions
Returns whether the link buffer is empty.
Examples
iex> link = ExSystolic.Link.new({{0,0}, :east}, {{1,0}, :west})
iex> ExSystolic.Link.empty?(link)
true
Creates a new link between two endpoints.
Examples
iex> link = ExSystolic.Link.new({{0,0}, :east}, {{1,0}, :west})
iex> link.from
{{0, 0}, :east}
iex> link.to
{{1, 0}, :west}
iex> link.latency
1
Peeks at the oldest value without removing it.
Examples
iex> link = ExSystolic.Link.new({{0,0}, :east}, {{1,0}, :west})
iex> {:ok, link2} = ExSystolic.Link.write(link, 7)
iex> ExSystolic.Link.peek(link2)
{:ok, 7}
Reads and removes the oldest value from the link buffer (FIFO).
Returns {value, link} when the buffer is non-empty, or {:empty, link}
when nothing has been written yet.
Note: the atom :empty is reserved as the empty-buffer marker -- do
not write it as a payload value. See the module documentation for
details.
Examples
iex> link = ExSystolic.Link.new({{0,0}, :east}, {{1,0}, :west})
iex> {:ok, link2} = ExSystolic.Link.write(link, 99)
iex> {val, _link3} = ExSystolic.Link.read(link2)
iex> val
99
iex> link = ExSystolic.Link.new({{0,0}, :east}, {{1,0}, :west})
iex> {result, _link} = ExSystolic.Link.read(link)
iex> result
:empty
@spec size(t()) :: non_neg_integer()
Returns the number of items currently in the buffer.
Examples
iex> link = ExSystolic.Link.new({{0,0}, :east}, {{1,0}, :west}, capacity: 3)
iex> ExSystolic.Link.size(link)
0
iex> {:ok, link2} = ExSystolic.Link.write(link, 1)
iex> {:ok, link3} = ExSystolic.Link.write(link2, 2)
iex> ExSystolic.Link.size(link3)
2
Advances the link by one tick: drains the buffer if latency has expired.
In the interpreted backend this is handled at the Array level; this
function exists for unit-testing the link in isolation. With latency=1
(the default), a value written at tick T is readable at tick T+1, so
tick/1 is effectively a no-op on the buffer itself -- the Clock
manages read-then-write ordering.
tick/1 preserves any buffered values; it does not discard them.
Examples
iex> link = ExSystolic.Link.new({{0,0}, :east}, {{1,0}, :west})
iex> ExSystolic.Link.tick(link) == link
true
iex> link = ExSystolic.Link.new({{0,0}, :east}, {{1,0}, :west})
iex> {:ok, link2} = ExSystolic.Link.write(link, 42)
iex> ExSystolic.Link.size(ExSystolic.Link.tick(link2))
1
Writes a value into the link buffer.
Returns {:ok, link} when the buffer has room, or {:error, :full} when
the buffer already contains capacity items.
Examples
iex> link = ExSystolic.Link.new({{0,0}, :east}, {{1,0}, :west})
iex> {:ok, link2} = ExSystolic.Link.write(link, 42)
iex> {val, _link3} = ExSystolic.Link.read(link2)
iex> val
42