A SvPortSim process owns exactly one simulator transport. The process
implementation lives in SvPortSim.Server; this module is the public facade
that keeps the stable user-facing API, validates command options, normalizes
command bodies, and delegates normalized runtime operations to the server.
In production the default transport opens one external wrapper process with
the port framing documented by SvPortSim.Protocol; tests and alternative
runtimes may provide their own SvPortSim.Transport implementation.
Public contract
The initial stable public API surface is returned by public_functions/0:
Default-argument arities such as reset/1, tick/1, poke/3, peek/2,
and stop/1 are exported for convenience.
Runtime commands return {:ok, body} for a successful wrapper response or
{:error, error_body} for wrapper-side and Elixir-side failures. Error
bodies follow the canonical shape documented by SvPortSim.Protocol and
include string keys such as "code", "message", "details", and
"fatal". stop/2 is terminal and returns :ok after a successful
shutdown.
Options
start_link/1 and start/1 accept these simulation options:
:executable- path to the generated simulator wrapper executable. This is required when using the defaultSvPortSim.Transport.Porttransport.:args- command-line arguments passed to the wrapper executable. Defaults to[].:transport- module implementingSvPortSim.Transport. Defaults toSvPortSim.Transport.Port.:transport_opts- extra keyword options passed to the transport. Defaults to[].:default_timeout- runtime command timeout in milliseconds or:infinity. Defaults to5_000.
Standard GenServer start options :name, :debug, :spawn_opt,
:hibernate_after, and :timeout are also accepted.
Runtime calls accept :timeout to override the instance default. reset/2
accepts :cycles and :reset; tick/2 accepts :cycles and :clock.
poke/4 accepts encoded bit-vector values in the shape %{bits: bits, width: width} or %{"bits" => bits, "width" => width}. bits must be a
string of 0, 1, x, or z characters whose length equals width.
Examples
iex> defmodule SvPortSim.DocTransport do
...> @behaviour Elixir.SvPortSim.Transport
...>
...> def open(_opts), do: {:ok, %{signals: %{}}}
...>
...> def request(%{"op" => "reset"} = request, state, _timeout) do
...> body = %{"cycles" => request["body"]["cycles"]}
...> {:ok, response(request, body), %{state | signals: %{}}}
...> end
...>
...> def request(%{"op" => "tick"} = request, state, _timeout) do
...> body = %{"cycles" => request["body"]["cycles"]}
...> {:ok, response(request, body), state}
...> end
...>
...> def request(%{"op" => "poke"} = request, state, _timeout) do
...> signal = request["body"]["signal"]
...> value = request["body"]["value"]
...> state = %{state | signals: Map.put(state.signals, signal, value)}
...> {:ok, response(request, %{"signal" => signal}), state}
...> end
...>
...> def request(%{"op" => "peek"} = request, state, _timeout) do
...> signal = request["body"]["signal"]
...> value = Map.get(state.signals, signal, %{"bits" => "0", "width" => 1})
...> {:ok, response(request, %{"value" => value}), state}
...> end
...>
...> def request(%{"op" => "shutdown"} = request, state, _timeout) do
...> {:ok, response(request, %{}), state}
...> end
...>
...> def close(_state), do: :ok
...>
...> defp response(request, body) do
...> %{
...> "v" => 1,
...> "id" => request["id"],
...> "kind" => "response",
...> "op" => request["op"],
...> "body" => body
...> }
...> end
...> end
iex> {:ok, sim} = Elixir.SvPortSim.start_link(transport: SvPortSim.DocTransport)
iex> Elixir.SvPortSim.reset(sim, cycles: 2)
{:ok, %{"cycles" => 2}}
iex> Elixir.SvPortSim.tick(sim)
{:ok, %{"cycles" => 1}}
iex> Elixir.SvPortSim.poke(sim, "enable", %{bits: "1", width: 1})
{:ok, %{"signal" => "enable"}}
iex> Elixir.SvPortSim.peek(sim, "enable")
{:ok, %{"value" => %{"bits" => "1", "width" => 1}}}
iex> Elixir.SvPortSim.stop(sim)
:ok
iex> Elixir.SvPortSim.start_link([])
{:error, {:missing_required_option, :executable}}
iex> {:reset, 2} in Elixir.SvPortSim.public_functions()
true
Summary
Types
Public API result returned by runtime commands except stop/2.
Encoded runtime bit-vector value accepted by poke/4.
Canonical runtime error body documented by SvPortSim.Protocol.
A running simulation instance pid, registered name, or via tuple.
Options accepted by reset/2.
Command response body returned by the wrapper.
A top-level SystemVerilog signal name exposed by the wrapper.
Options accepted by start_link/1, start/1, and child_spec/1.
Options accepted by tick/2.
Public API timeout in milliseconds or :infinity.
Functions
Returns a child spec for supervising one simulation instance.
Reads a signal from the simulator.
Writes an encoded value to a signal.
Returns the stable public API surface for one simulation instance.
Resets the simulator.
Starts one simulation instance without linking it to the caller.
Starts one simulation instance and links it to the caller.
Sends the terminal shutdown command and stops the simulation instance.
Advances the simulator by one or more clock cycles.
Types
@type api_result() :: {:ok, response_body()} | {:error, error_body()}
Public API result returned by runtime commands except stop/2.
@type command_option() :: {:timeout, timeout_ms()}
@type encoded_value() :: %{ optional(:bits) => String.t(), optional(:width) => pos_integer(), optional(String.t()) => String.t() | pos_integer() }
Encoded runtime bit-vector value accepted by poke/4.
Canonical runtime error body documented by SvPortSim.Protocol.
@type instance() :: GenServer.server()
A running simulation instance pid, registered name, or via tuple.
@type reset_option() :: command_option() | {:cycles, pos_integer()} | {:reset, signal()} | {:clock, signal()}
Options accepted by reset/2.
Command response body returned by the wrapper.
A top-level SystemVerilog signal name exposed by the wrapper.
@type start_option() :: {:executable, Path.t()} | {:args, [String.t()]} | {:transport, module()} | {:transport_opts, keyword()} | {:default_timeout, timeout()} | {:name, GenServer.name()} | {:debug, term()} | {:spawn_opt, [term()]} | {:hibernate_after, non_neg_integer()} | {:timeout, timeout_ms()} | {:id, term()}
Options accepted by start_link/1, start/1, and child_spec/1.
@type tick_option() :: command_option() | {:cycles, pos_integer()} | {:clock, signal()}
Options accepted by tick/2.
@type timeout_ms() :: pos_integer() | :infinity
Public API timeout in milliseconds or :infinity.
Functions
@spec child_spec([start_option()]) :: Supervisor.child_spec()
Returns a child spec for supervising one simulation instance.
Pass :id to override the child id. Other options are passed to
start_link/1.
@spec peek(instance(), signal(), [command_option()]) :: api_result()
Reads a signal from the simulator.
@spec poke(instance(), signal(), encoded_value(), [command_option()]) :: api_result()
Writes an encoded value to a signal.
encoded_value must be %{bits: bits, width: width} or
%{"bits" => bits, "width" => width}. The value is normalized to
string-keyed JSON-compatible data before it is sent to the transport.
@spec public_functions() :: [{atom(), non_neg_integer()}]
Returns the stable public API surface for one simulation instance.
Internal server functions are intentionally not included in this list.
Examples
iex> SvPortSim.public_functions() |> Enum.member?({:start_link, 1})
true
iex> SvPortSim.public_functions() |> Enum.member?({:poke, 4})
true
@spec reset(instance(), [reset_option()]) :: api_result()
Resets the simulator.
Options:
:cycles- number of reset cycles. Defaults to1.:reset- reset signal name. Defaults to wrapper policy.:clock- clock signal name. Defaults to wrapper policy.:timeout- per-request timeout.
@spec start([start_option()]) :: GenServer.on_start()
Starts one simulation instance without linking it to the caller.
@spec start_link([start_option()]) :: GenServer.on_start()
Starts one simulation instance and links it to the caller.
@spec stop(instance(), [command_option()]) :: :ok | {:error, error_body()}
Sends the terminal shutdown command and stops the simulation instance.
Returns :ok after the wrapper acknowledges shutdown and the transport is
closed. Returns {:error, error_body} if the shutdown request fails.
@spec tick(instance(), [tick_option()]) :: api_result()
Advances the simulator by one or more clock cycles.
Options:
:cycles- number of cycles. Defaults to1.:clock- clock signal name. Defaults to wrapper policy.:timeout- per-request timeout.